<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Three.js实现简单开门动画</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
</head>

<body>
    <script type="importmap">
        {
            "imports": {
                "three": "./build/three.module.js",
                "three/addons/": "./examples/jsm/"
            }
        }
    </script>
    <script type="module">
        import * as THREE from 'three';
        import Stats from 'three/addons/libs/stats.module.js';
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

        let raycaster = new THREE.Raycaster();
        let timer = null;
        const stats = new Stats(); // 性能监控器，用来查看Three.js渲染帧率

        // 创建div
        const container = document.createElement('div');
        document.body.appendChild(container);

        // 创建场景
        const scene = new THREE.Scene();

        // 创建时钟
        const clock = new THREE.Clock();

        const axesHelper = new THREE.AxesHelper(100);
        // scene.add(axesHelper)


        // 创建相机  透视投影相机
        const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 3000);
        camera.position.set(-10, 50, 200); // 设置相机位置

        // 创建渲染器
        const renderer = new THREE.WebGLRenderer({
            antialias: true, // 是否执行抗锯齿
        });
        renderer.setPixelRatio(window.devicePixelRatio); // 设置设备像素比率。通常用于HiDPI设备，以防止输出画布模糊。
        renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器大小
        renderer.shadowMap.enabled = true;
        container.appendChild(renderer.domElement);

        // 创建控制器
        const controls = new OrbitControls(camera, renderer.domElement);

        // 创建平面
        const planeGeometry = new THREE.PlaneGeometry(300, 300); // 生成平面几何
        const planeMaterial = new THREE.MeshLambertMaterial({
            // 生成材质
            color: 0xcccccc,
        });
        const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial); // 生成平面网格
        planeMesh.rotation.x = -Math.PI / 2; //绕X轴旋转90度
        scene.add(planeMesh); // 添加到场景中

        // 创建平行光源
        const light = new THREE.DirectionalLight(0xffffff, 1); // 平行光，颜色为白色，强度为1
        light.position.set(-40, 40, 20); // 设置灯源位置
        scene.add(light); // 添加到场景中

        // 创建门框
        const doorFrameSide = new THREE.BoxBufferGeometry(1, 20, 2); // 侧边门框
        const doorFrameTop = new THREE.BoxBufferGeometry(12, 1, 2); // 上门框
        const doorFrameMaterial = new THREE.MeshLambertMaterial({
            color: 0xad4800,
        });
        const doorFrameLeftMesh = new THREE.Mesh(doorFrameSide, doorFrameMaterial);
        const doorFrameTopMesh = new THREE.Mesh(doorFrameTop, doorFrameMaterial);
        const doorFrameRightMesh = doorFrameLeftMesh.clone();
        doorFrameLeftMesh.position.set(-5.5, 10, 0);
        doorFrameRightMesh.position.set(5.5, 10, 0);
        doorFrameTopMesh.position.set(0, 20.5, 0);
        scene.add(doorFrameLeftMesh);
        scene.add(doorFrameRightMesh);
        scene.add(doorFrameTopMesh);

        // 创建门
        const door = new THREE.BoxBufferGeometry(10, 20, 0.5);
        const doorMaterial = new THREE.MeshLambertMaterial({ color: 0xd88c00 });
        const doorMesh = new THREE.Mesh(door, doorMaterial);
        // doorMesh.position.set(0, 10, 0);
        doorMesh.name = "door";
        scene.add(doorMesh);

        // 创建墙体
        const generateWall = ({ x, y, z }) => {
            const wall = new THREE.BoxBufferGeometry(x, y, z);
            const wallMaterial = new THREE.MeshLambertMaterial({ color: "#F4A460" });
            const wallMesh = new THREE.Mesh(wall, wallMaterial);
            return wallMesh;
        }

        // 创建墙体
        const wallLeftMesh = generateWall({ x: .5, y: 30, z: 60, })
        const wallRightMesh = generateWall({ x: .5, y: 30, z: 60, })
        const wallAfterMesh = generateWall({ x: 80, y: 30, z: 0.5, })
        const wallTopMesh = generateWall({ x: 80, y: .5, z: 60, })
        const wallFront1Mesh = generateWall({ x: 80, y: 10, z: 0.5, })
        const wallFront2Mesh = generateWall({ x: 34, y: 20, z: 0.5, })
        const wallFront3Mesh = wallFront2Mesh.clone();
        wallLeftMesh.position.set(-40, 15, -30);
        wallRightMesh.position.set(40, 15, -30);
        wallAfterMesh.position.set(0, 15, -60);
        wallTopMesh.position.set(0, 30, -30);
        wallFront1Mesh.position.set(0, 25, 0);
        wallFront2Mesh.position.set(23, 10, 0);
        wallFront3Mesh.position.set(-23, 10, 0);

        scene.add(wallLeftMesh);
        scene.add(wallRightMesh);
        scene.add(wallAfterMesh);
        scene.add(wallTopMesh);
        scene.add(wallFront1Mesh);
        scene.add(wallFront2Mesh);
        scene.add(wallFront3Mesh);


        // 创建集合体
        const group = new THREE.Group();
        group.position.set(5, 10, 0);// 设置外层对象的中心为原本想要旋转的位置
        doorMesh.position.set(-5, 0, 0);
        group.add(doorMesh);
        scene.add(group);

        document.addEventListener("click", event => {
            clearInterval(timer)
            const mouse = new THREE.Vector2();
            mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
            mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
            raycaster.setFromCamera(mouse, camera);
            const intersects = raycaster.intersectObjects(scene.children, true);

            if (intersects.length > 0 && intersects[0].object.name === 'door') {

                if (group.rotation.y == 0) {

                    timer = setInterval(() => {

                        if (Math.abs(group.rotation.y) >= 1.2)
                            return clearInterval(timer)

                        group.rotation.y += -0.01;

                    }, 10.7)

                } else {
                    timer = setInterval(() => {

                        group.rotation.y += 0.01;

                        if (group.rotation.y >= 0) {
                            clearInterval(timer)
                            group.rotation.y = 0
                        }

                    }, 10.7)
                }

            }
        });

        render();

        function render() {
            requestAnimationFrame(render);
            // stats.begin();
            renderer.render(scene, camera);
            // mixer.update(clock.getDelta());
            // stats.end();
        }
    </script>
</body>

</html>